package com.fanshe.classDemo;

import com.fanshe.Person;
import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ClassTestDemo {
    //二.通过反射机制获取信息
    //1.构造函数
    @Test
    public void test1() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取字节码文件
        Class<?> classs = Class.forName("com.fanshe.Person");
        //先获取有参构造，paramterTypes" 表达参数列表，也可以不写
        Constructor<?> constructor = classs.getConstructor(String.class, int.class);
        //通过构造器来实例化参数
        Person person = (Person)constructor.newInstance("小轩", 15);
    }

    /**
     * 反射获取全部构造函数
     */
    @Test
    public void test2() throws Exception {
        //获取字节码文件
        Class aClass = Class.forName("com.fanshe.Person");

        //获取所有的构造函数
        Constructor[] constructors = aClass.getConstructors();
        //遍历所有构造函数
        for(int i = 0; i < constructors.length; i++) {
            Class[] parameterTypes = constructors[i].getParameterTypes();
            System.out.println("第" + i + "个构造函数: ");
            for(int j = 0; j < parameterTypes.length; j++) {
                System.out.println(parameterTypes[j].getName() + ",");
            }
        }
    }

    /**
     * 2.属性
     * 在学习spring ioc之时，对未提供set方法的private属性依然可以注入感到神奇万分，现在看来，这神奇的根源自然是来自于java的反射。
     */
    @Test
    public void test3() throws Exception {
        //获取字节码文件
        Class<?> aClass = Class.forName("com.fanshe.Person");
        //获取实例对象
        Person person = (Person)aClass.newInstance();

        //获取成员变量class.getField(name);通过name来获取指定成员变量
        //如果该成员变量是私有的，则应该使用getDeclaredField("userName");
        Field declaredField = aClass.getDeclaredField("name");
        //因为属性是私有的，获得其对象后，还有让打开可见权限
        declaredField.setAccessible(true);
        //对成员变量进行操作
        //赋值操作
        declaredField.set(person, "xuehu");
        System.out.println(person.getName());

    }

    /**
     * 2.2 通过反射获取全部属性
     */
    @Test
    public void test4() throws Exception{
        //获取字节码文件
        Class<?> aClass = Class.forName("com.fanshe.Person");

        //获取其实例对象
        Person person = (Person) aClass.newInstance();

        //赋值操作
        person.setName("xuehu");
        person.setAge(12);

        //将私有属性一并获得
        Field[] fields = aClass.getDeclaredFields();

        //遍历所有的私有属性
        for(int i = 0; i < fields.length; i++) {
            //打开可见权限
            fields[i].setAccessible(true);
            System.out.println(fields[i].get(person));
        }
    }

    /**
     * 3.方法
     * 3.1 不带参数的方法
     */
    @Test
    public void test5() throws Exception {
        //获取字节码文件
        Class<?> aClass = Class.forName("com.fanshe.Person");

        //获取对象
        Person person = (Person) aClass.newInstance();

        //不带参数的方法,name为不带参数的方法
        /**
         * class.getMethod(name,paraMeterTypes)
         * name:方法的名称
         * parameterTypes: 方法的参数类型，没有则什么都不填，列如:String.class
         */
        Method method = aClass.getMethod("show");
        //调用方法
        /**
         * method.invoke(obj, args)
         * obj:方法的对象
         * args:实际的参数值，没有则不填
         */
        method.invoke(person);

    }

    /**
     * 3.2带参数的方法
     */
    @Test
    public void test6() throws Exception {

        //获取字节码文件
        Class<?> aClass = Class.forName("com.fanshe.Person");

        //获取其实例对象
        Person person = (Person)aClass.newInstance();

        //获取带参数的方法，为方法名
        Method method = aClass.getDeclaredMethod("demo", int.class);
        //设置可见性
        method.setAccessible(true);
        //调用方法
        method.invoke(person, 2);
    }

    /**
     * 3.3 获取所有的方法
     */
    @Test
    public void test7() throws Exception {
        //存取字节码文件
        Class<?> aClass = Class.forName("com.fanshe.Person");
        //获取其实例对象
        Person person = (Person) aClass.newInstance();
        //获取所有的方法
        Method[] methods = aClass.getMethods();
        //遍历所有的方法
        for(Method method : methods) {
            //设置可见性
            method.setAccessible(true);
            System.out.println(method.getName());
            //获得方法的参数
            Class<?>[] parameterTypes = method.getParameterTypes();
            for(int i = 0; i < parameterTypes.length; i++) {
                //获得构造函数中的参数类型
                System.out.println(parameterTypes[i].getName() + ",");
            }
        }
    }





}
